//
//  LKCaughtExceptionHandler.m
//  CaughtException
//
//  Created by ZhangXiaofei on 16/9/7.
//  Copyright © 2016年 Yuri. All rights reserved.
//

#import "LKCaughtExceptionHandler.h"
#import "EMNetworking.h"


#define kDEF_EXCEPTION_URL @"http://192.168.1.46:8080/client/errorInfo"
#define kDEF_ERROR_KEY @"errordata"

NSString * applicationDocumentsDirectory() {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

// 崩溃时的回调函数
void UncaughtExceptionHandler(NSException * exception) {
    
    NSDictionary *deviceInfoDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"LKAnalyticsDeviceInfo"];
    
    NSArray *arr = [exception callStackSymbols];
    NSString *reason = [exception reason]; //崩溃的原因  可以有崩溃的原因(数组越界,字典nil,调用未知方法...) 崩溃的控制器以及方法
    NSString *name = [exception name];
    
    NSDate *datenow = [NSDate date];
    NSDateFormatter *formater = [[NSDateFormatter alloc] init];
    [formater setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *dateStr = [formater stringFromDate:datenow];
    
    NSString *deviceName = deviceInfoDict[@"deviceName"];
    NSString *operating = [NSString stringWithFormat:@"%@ %@", deviceInfoDict[@"OSName"], deviceInfoDict[@"OSVersion"]];
    NSString *appVersion = deviceInfoDict[@"appVersion"];
    
    NSString *url = [NSString stringWithFormat:@"========异常错误报告========\nDeviceName: %@\nOperating: %@\nAppVersion: %@\nTime: %@\nName: %@\nReason:\n%@\nCallStackSymbols:\n%@", deviceName, operating, appVersion, dateStr, name, reason, [arr componentsJoinedByString:@"\n"]];
    
    NSString *createPath = [NSString stringWithFormat:@"%@/Exceptions", applicationDocumentsDirectory()];
    if (![[NSFileManager defaultManager] fileExistsAtPath:createPath]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:createPath withIntermediateDirectories:YES attributes:nil error:nil];
    } else {
//        NSLog(@"FileDir is exists.");
    }
    
    NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]];

    NSString *fileName = [NSString stringWithFormat:@"%@.txt", timeSp];

    NSString *path = [createPath stringByAppendingPathComponent:fileName];
    // 将一个txt文件写入沙盒
    [url writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
    
}

@implementation LKCaughtExceptionHandler

// 沙盒地址
-(NSString *)applicationDocumentsDirectory {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

+ (void)setDefaultHandler {
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
    NSArray *files = [self getCrashFilenamelist];
    if (files.count > 0) {
        for (NSString *fileName in files) {
            NSString *path = [NSString stringWithFormat:@"%@/Exceptions/%@", applicationDocumentsDirectory(), fileName];

//            NSData *data = [NSData dataWithContentsOfFile:path];
            NSString *str = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//            NSLog(@"%@", str);
            [self uploadData:str name:fileName];
        }
    }
}

+ (NSUncaughtExceptionHandler *)getHandler {
    return NSGetUncaughtExceptionHandler();
}

+ (void)takeException:(NSException *)exception {
    NSArray *arr = [exception callStackSymbols];
    
    NSDate *datenow = [NSDate date];
    NSDateFormatter *formater = [[NSDateFormatter alloc] init];
    [formater setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *dateStr = [formater stringFromDate:datenow];
    
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    NSString *url = [NSString stringWithFormat:@"========异常错误报告========\ntime:%@\n\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",dateStr,name,reason,[arr componentsJoinedByString:@"\n"]];
    NSString *path = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"Exception.txt"];
    [url writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

+ (NSArray *)getCrashFilenamelist {
    NSString *path = [NSString stringWithFormat:@"%@/Exceptions", applicationDocumentsDirectory()];

    NSArray *fileList = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil] pathsMatchingExtensions:[NSArray arrayWithObject:@"txt"]];
    return fileList;
}

+ (void)uploadData:(NSString *)dataStr name:(NSString *)fileName {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSDictionary *deviceInfoDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"LKAnalyticsDeviceInfo"];
        if (!deviceInfoDict) {
            return;
        }
        //设备名
        NSString *deviceName = deviceInfoDict[@"deviceName"];
        //设备操作系统版本号
        NSString *operatingVersion = [NSString stringWithFormat:@"iOS %@", deviceInfoDict[@"OSVersion"]];
        //app版本号
        NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
        //错误时间戳
        NSString *timeStamp = [fileName componentsSeparatedByString:@"."].firstObject;
        //app ID
        NSString *appID = [[NSUserDefaults standardUserDefaults] objectForKey:@"LKAnalyticesAppKey"];
        //错误信息
        NSString *errorInfo = [self JSONString:[dataStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
        NSString *urlStr;
        
        if (([[[NSUserDefaults standardUserDefaults] objectForKey:@"LKAnalyticesDevUrl"] integerValue] == 1)||(![[NSUserDefaults standardUserDefaults] objectForKey:@"LKAnalyticesDevUrl"])||([[NSUserDefaults standardUserDefaults] objectForKey:@"LKAnalyticesDevUrl"] == NULL||([NSString stringWithFormat:@"%@",[[NSUserDefaults standardUserDefaults] objectForKey:@"LKAnalyticesDevUrl"]].length == 0))) {
            urlStr = @"http://192.168.1.46:8080";
        }else {
            urlStr = [[NSUserDefaults standardUserDefaults] objectForKey:@"LKAnalyticesDevUrl"];
        }
//        NSLog(@"url:%@",urlStr);
        NSDictionary *dic = @{@"deiceName":deviceName,@"sysVersion":operatingVersion,@"errorTime":timeStamp,@"appId":appID,@"errorInfo":errorInfo,@"appVersion":appVersion};
        
        EMURLSessionManager *manager = [[EMURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
        NSMutableURLRequest *req = [[EMJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:[NSString stringWithFormat:@"%@/boss/tracking/event/crash",urlStr] parameters:nil error:nil];
        req.timeoutInterval= [[[NSUserDefaults standardUserDefaults] valueForKey:@"timeoutInterval"] longValue];
        [req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        [req setValue:@"application/json" forHTTPHeaderField:@"Accept"];
        NSString *logTempArrayMStr = [self dictionaryToJson:dic];
        [req setHTTPBody:[logTempArrayMStr dataUsingEncoding:NSUTF8StringEncoding]];
        
        [[manager dataTaskWithRequest:req uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
            if (!error) {
                if ([[responseObject objectForKey:@"statusCode"] integerValue] == 200) {
//                    NSLog(@"崩溃发送:%@", responseObject);
                    NSString *path = [NSString stringWithFormat:@"%@/Exceptions", applicationDocumentsDirectory()];
                    [[NSFileManager defaultManager] removeItemAtPath:[path stringByAppendingPathComponent:fileName] error:nil];
                } else {
//                    NSLog(@"崩溃发送:%@",[responseObject objectForKey:@"statusMessage"]);
                }
            } else {
//                NSLog(@"崩溃发送:%@",error);
            }
        }] resume];
    });
    
}

+ (NSString *)JSONString:(NSString *)aString {
    NSMutableString *s = [NSMutableString stringWithString:aString];
    [s replaceOccurrencesOfString:@"\"" withString:@"\\\"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
    [s replaceOccurrencesOfString:@"/" withString:@"\\/" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
    [s replaceOccurrencesOfString:@"\n" withString:@"\\n" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
    [s replaceOccurrencesOfString:@"\b" withString:@"\\b" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
    [s replaceOccurrencesOfString:@"\f" withString:@"\\f" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
    [s replaceOccurrencesOfString:@"\r" withString:@"\\r" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
    [s replaceOccurrencesOfString:@"\t" withString:@"\\t" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
    return [NSString stringWithString:s];
}

/**
 *  格式化字符串
 *
 *  @param params 参数字典
 *
 *  @return 格式化字符串, username=LK&pwd=LK&type=JSON
 */
+ (NSString *)parseParams:(NSDictionary *)params {
    NSString *keyValueFormat;
    NSMutableString *result = [NSMutableString new];
    NSMutableArray *array = [NSMutableArray new];
    //实例化一个key枚举器用来存放dictionary的key
    NSEnumerator *keyEnum = [params keyEnumerator];
    id key;
    while (key = [keyEnum nextObject]) {
        keyValueFormat = [NSString stringWithFormat:@"%@=%@", key, [params valueForKey:key]];
        [result appendString:keyValueFormat];
        [array addObject:keyValueFormat];
    }
    return result;
}

+ (NSString *)dictionaryToJson:(id)object {
    NSString *jsonString = [[NSString alloc]init];
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:&error];
    if (! jsonData) {
//        NSLog(@"error: %@", error);
    } else {
        jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }
    NSMutableString *mutStr = [NSMutableString stringWithString:jsonString];
    NSRange range = {0,jsonString.length};
    [mutStr replaceOccurrencesOfString:@" "withString:@""options:NSLiteralSearch range:range];
    NSRange range2 = {0,mutStr.length};
    [mutStr replaceOccurrencesOfString:@"\n"withString:@""options:NSLiteralSearch range:range2];
    return mutStr;
}

@end
